﻿#region

using System.Collections.Generic;
using System.IO;
using System.Linq;
using CDNSync.Providers;
using CDNSync.Providers.Model;

#endregion

namespace CDNSync
{
    public class Synchronizer : ISynchronizer
    {
        private readonly IProviderClientFactory providerClientFactory;
        private IProviderClient providerClient;

        public Synchronizer(IProviderClientFactory providerClientFactory)
        {
            this.providerClientFactory = providerClientFactory;
        }

        public Synchronizer() : this(new ProviderClientFactory())
        {
        }

        #region ISynchronizer Members

        public SyncFilesResult SyncFiles(SyncFilesRequest syncFilesRequest)
        {
            var syncFilesResult = new SyncFilesResult();

            providerClient = providerClientFactory.Create(syncFilesRequest.Provider);

            var authenticated = providerClient.Authenticate(syncFilesRequest.UserName, syncFilesRequest.Password);

            if (!authenticated)
            {
                syncFilesResult.AddError("Invalid credentials supplied");
            }

            var createBucketResult = CreateBucketIfNotExists(syncFilesRequest.BucketName, syncFilesRequest.Region);

            if (!createBucketResult.WasSuccessful)
            {
                syncFilesResult.AddErrors(createBucketResult.Errors);
                return syncFilesResult;
            }

            syncFilesResult.AddLogMessages(createBucketResult.LogMessages);

            IEnumerable<FileUploadRequest> files = new List<FileUploadRequest>();

            if (syncFilesRequest.Files != null && syncFilesRequest.Files.Count() > 0)
            {

                files = files.Union(from f in syncFilesRequest.Files
                                    select new 
                                        FileUploadRequest(f, syncFilesRequest.UseFolderNameInKey, 
                                        string.Empty, syncFilesRequest.FileNameSuffix, syncFilesRequest.FileNamePrefix, syncFilesRequest.GZipExtensions != null 
                                        ? syncFilesRequest.GZipExtensions.Any(f.EndsWith) : false));
            }

            if (syncFilesRequest.Folders != null && syncFilesRequest.Folders.Count() > 0)
            {
                foreach (var dirInfo in syncFilesRequest.Folders.Select(directory => new DirectoryInfo(Path.GetFullPath(directory))).Where(dirInfo => dirInfo.Exists))
                {
                    var dirFiles = GetFilesFromDirectory(dirInfo, syncFilesRequest.UseFolderNameInKey, syncFilesRequest.Recursive, string.Empty, syncFilesRequest.FileNameSuffix, syncFilesRequest.FileNamePrefix, syncFilesRequest.GZipExtensions);
                    files = files.Union(dirFiles);
                }
            }


            var uploadResult = providerClient.Upload(files, syncFilesRequest.BucketName);

            syncFilesResult.AddLogMessages(uploadResult.LogMessages);

            if (!uploadResult.WasSuccessful)
            {
                syncFilesResult.AddErrors(uploadResult.Errors);
            }
            return syncFilesResult;
        }

        #endregion

        #region Private Methods 

        private IEnumerable<FileUploadRequest> GetFilesFromDirectory(DirectoryInfo dirInfo,
                                                                     bool useFolderNameInKey,
                                                                     bool recursive, 
                                                                     string parentFolder, 
                                                                     string fileNameSuffix, 
                                                                     string fileNamePrefix, 
                                                                     IEnumerable<string> gZipExtensions)
        {
            var files = from f in dirInfo.GetFiles()
                        select new FileUploadRequest(f.FullName, useFolderNameInKey, parentFolder, fileNameSuffix, fileNamePrefix, gZipExtensions.Any(f.FullName.EndsWith));

            if (!recursive)
            {
                return files;
            }

            foreach (var subDirectory in dirInfo.GetDirectories())
            {
                if (subDirectory.Parent == null)
                {
                    continue; 
                }

                var dirFiles = GetFilesFromDirectory(subDirectory, useFolderNameInKey, true, Path.Combine(parentFolder, subDirectory.Parent.Name), fileNameSuffix, fileNamePrefix, gZipExtensions);

                files = files.Union(dirFiles);
            }

            return files;
        }

        private CreateBucketResult CreateBucketIfNotExists(string bucketName, BucketRegion region)
        {
            var bucketExists = providerClient.BucketExists(bucketName);

            return bucketExists ? new CreateBucketResult() : providerClient.CreateBucket(bucketName, region);
        }

        #endregion
    }
}